/*
 * condition.cpp
 *
 *  Created on: 2008-12-26
 *      Author: ant
 */

#include "condition.h"
#include <algorithm>
#include <functional>

BEGIN_NAMESPACE_TOXIC

Condition::Condition()
:_mutex()
,_totalCount(0)
,_generations()
,_wakeSemaphore(0) {

}

Condition::~Condition() {

}

void Condition::wait(ScopedLock& lock) {
	wait(lock, INFINITE);
}

bool Condition::wait(ScopedLock& lock, unsigned long millisecond) {
	EntryPointer entry(getWaitEntry());
	lock.unlock();
	bool woken = entry->wait(millisecond);
	entry->removeWaiter();
	lock.lock();
	return woken;
}

void Condition::notify() {
	if (InterlockedReadAcquire(&_totalCount)) {
		ScopedLock lock(_mutex);
		if (!_totalCount) {
			return;
		}
		wakeWaiters(1);
		for (GenerationList::iterator it = _generations.begin(), end = _generations.end(); it != end; ++it) {
			(*it)->release(1);
		}
		_generations.erase(std::remove_if(_generations.begin(), _generations.end(), &ListEntry::noWaiters), _generations.end());
	}
}

void Condition::notifyAll() {
	if (InterlockedReadAcquire(&_totalCount)) {
		ScopedLock lock(_mutex);
		if (!_totalCount) {
			return;
		}
		wakeWaiters(_totalCount);
		for (GenerationList::iterator it = _generations.begin(), end = _generations.end(); it != end; ++it) {
			(*it)->releaseWaiters();
		}
		_generations.clear();
		_wakeSemaphore = 0;
	}
}

void Condition::wakeWaiters(long count) {
	InterlockedWriteRelease(&_totalCount, _totalCount - count);
	ReleaseSemaphore(_wakeSemaphore, count, 0);
}

Condition::EntryPointer Condition::getWaitEntry() {
	ScopedLock lock(_mutex);

	if (!_wakeSemaphore) {
		_wakeSemaphore = CreateSemaphore(0, 0, LONG_MAX, 0);
	}

	InterlockedWriteRelease(&_totalCount,_totalCount + 1);
	if (_generations.empty() || _generations.back()->isNotified()) {
		EntryPointer entry(new ListEntry(_wakeSemaphore));
		_generations.push_back(entry);
		return entry;
	}
	else {
		_generations.back()->addWaiter();
		return _generations.back();
	}
}

END_NAMESPACE_TOXIC
